package com.k2data.k2app.service;

import com.google.common.collect.Lists;
import com.k2data.app.kmx.KmxClient;
import com.k2data.app.kmx.cond.KmxCond;
import com.k2data.app.kmx.domain.DataRows;
import com.k2data.app.kmx.domain.DataRowsResults;
import com.k2data.app.kmx.domain.Field;
import com.k2data.app.kmx.domain.RowDomain;
import com.k2data.app.kmx.enums.ValueType;
import com.k2data.k2app.common.KmxParams;
import com.k2data.k2app.common.MyConstant;
import com.k2data.k2app.constant.Constant;
import com.k2data.k2app.domain.po.TDevice;
import com.k2data.k2app.domain.po.TMonitorLimitValue;
import com.k2data.k2app.domain.po.TMonitorPoint;
import com.k2data.k2app.domain.po.TMonitorVariable;
import com.k2data.k2app.mapper.TMonitorLimitValueMapper;
import com.k2data.k2app.mapper.TMonitorPointMapper;
import com.k2data.k2app.mapper.TMonitorVariableMapper;
import com.k2data.k2app.utils.StringUtils;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author lidong9144@163.com 17-4-13.
 */
@Log4j2
@Service
public class KmxService {

    private final KmxParams kmxParams;
    private final TMonitorVariableMapper tMonitorVariableMapper;
    private final TMonitorPointMapper tMonitorPointMapper;
    private final TMonitorLimitValueMapper tMonitorLimitValueMapper;
    private final KmxFieldGroupService kmxFieldGroupService;
    private final KmxFieldService kmxFieldService;
    private final KmxRuleService kmxRuleService;

    private volatile LocalDateTime kmxStartTime; // 存储 kmx 第一次收到数据的时间, 这个时间一般不会变化, 所以不刷新, 只初始化一次
    private static final Object kmxStartTimeLock = new Object();

    @Autowired
    MyConstant myConstant;
    @Autowired
    public KmxService(KmxParams kmxParams, TMonitorVariableMapper tMonitorVariableMapper,
                      TMonitorPointMapper tMonitorPointMapper, TMonitorLimitValueMapper tMonitorLimitValueMapper,
                      KmxFieldGroupService kmxFieldGroupService, KmxFieldService kmxFieldService,
                      KmxRuleService kmxRuleService) {
        this.kmxParams = kmxParams;
        this.tMonitorVariableMapper = tMonitorVariableMapper;
        this.tMonitorPointMapper = tMonitorPointMapper;
        this.tMonitorLimitValueMapper = tMonitorLimitValueMapper;
        this.kmxFieldGroupService = kmxFieldGroupService;
        this.kmxFieldService = kmxFieldService;
        this.kmxRuleService = kmxRuleService;
    }

    /**
     * 同步元数据到 kmx
     */
    @Transactional
    public int toKmx(Long pointId) throws InterruptedException {
        TMonitorPoint cond = new TMonitorPoint();
        cond.setId(pointId);

        List<TMonitorPoint> list = tMonitorPointMapper.select(cond);
        for (TMonitorPoint point : list) {
            point2Kmx(point);
        }

        return 1;
    }

    @Transactional  // 以下需要独立事务，因为 kmx 不能回滚
    public void point2Kmx(TMonitorPoint point) throws InterruptedException {
        TMonitorVariable varCond = new TMonitorVariable();
        varCond.setMonitorPointId(point.getId());
        varCond.setState(0);
        List<TMonitorVariable> varList = new ArrayList<>();

        //本次变量
        List<TMonitorVariable> varListThis = tMonitorVariableMapper.select(varCond);

        // kmx已经同步的变量
       String[] ids = point.getNameEn().split("_");
        String id = ids[0]+"_"+ids[1];
        List<TMonitorVariable> varListKmx = tMonitorVariableMapper.selectVariablesKmx(id);
        List<String> kmxVariableNameEnKmx = new ArrayList<>();
        for(TMonitorVariable t:varListKmx){
            kmxVariableNameEnKmx.add(t.getKmxVariableNameEn());
        }

        //去重
        for(TMonitorVariable t:varListThis){
            if(!kmxVariableNameEnKmx.contains(t.getKmxVariableNameEn())){
                varList.add(t);
            }else{
                tMonitorVariableMapper.updateState(point.getId(), t.getKmxVariableNameEn(), 1);
            }
        }

        List<Field> fields = Lists.newArrayList()   ;
        for (TMonitorVariable var : varList) {
            Field field = new Field();
            field.setId(var.getKmxVariableNameEn());
            field.setName(var.getKmxVariableNameEn());
            field.setDescription(var.getKmxVariableName());
            field.setIdField(false);
            if (!StringUtils.isBlank(var.getKmxUnit())) {
                field.setUnit(var.getKmxUnit());
            }
            field.setValueType(ValueType.valueOf(var.getKmxVariableType()));
            if (var.getFrequency() != 0) {
                field.setIntervals(Lists.newArrayList(var.getFrequency()));
            }
            fields.add(field);
        }

        boolean exist = (point.getState() == 1);
        if (!exist) {
            addIdField(fields);
            kmxFieldGroupService.addFieldGroup(point, fields);
        }
        if (exist) {
            kmxFieldService.addField(point, fields);
        }

        List<TMonitorLimitValue> limits = tMonitorLimitValueMapper.selectLimit(point.getId(), 0);
        for (TMonitorLimitValue limit : limits) {
            kmxRuleService.pushLimitValue(limit);
        }
    }

    /**
     * 获取 kmx 第一次接到数的时间, 第一次会初始化缓存
     * <p> TODO kmx 没有现成的接口, 所以循环所有 fieldGroup  2017-06-01
     */
    public LocalDateTime getKmxStartTime() {
        if (kmxStartTime != null) {
            return kmxStartTime;
        }

        synchronized (kmxStartTimeLock) {
            if (kmxStartTime != null) {
                return kmxStartTime;
            }

            Long startTime = Long.MAX_VALUE;

            TDevice cond = new TDevice();
            cond.setIsRoot(Constant.YES);
            for (TMonitorPoint point : tMonitorPointMapper.selectAll()) {
                String fieldGroup = myConstant.getFieldGroup(point.getNameEn());
                KmxCond kmxCond = KmxCond.dataRows(kmxParams.getKmxInitParams())
                        .fieldGroup(fieldGroup)
                        .field(MyConstant.KMX_ID_FIELD_FACTORY_NAME)
                        .idValue(point.getId()+"")
                        .start(new Date(1273481978000L))    // 2010/5/10 16:59:38
                        .end(new Date())
                        .size(1)
                        .page(1)
                        .build();

                DataRows dataRows;
                try {
                    dataRows = KmxClient.sync(kmxCond);
                } catch (Exception e) {
                    // 这里忽略掉错误, 不管 fieldGroup 是否注册都继续执行
                    log.error("", e);
                    continue;
                }

                for (DataRowsResults dataRowsResults : dataRows.getResults()) {
                    if (dataRowsResults.getRows().isEmpty()) {
                        continue;
                    }

                    RowDomain row = dataRowsResults.getRows().get(0);
                    if (row.getTimestamp() == null) {
                        continue;
                    }

                    if (row.getTimestamp() < startTime) {
                        startTime = row.getTimestamp();
                    }
                }
            }

            if (startTime == Long.MAX_VALUE) {
                kmxStartTime = null;
            } else {
                kmxStartTime = LocalDateTime.ofInstant(new Date(startTime).toInstant(), ZoneId.systemDefault());
            }

            return kmxStartTime;
        }
    }

    public boolean fieldGroupExist(String fieldGroupId) {
        KmxCond kmxCond = KmxCond.fieldGroups(kmxParams.getKmxInitParams())
                .get()
                .id(fieldGroupId)
                .build();

        try {
            KmxClient.sync(kmxCond);
        } catch (Exception e) {
            log.error("", e);
            return false;
        }

        return true;
    }

    public boolean ruleExist(String fieldGroupId, String fieldId) {
        KmxCond cond = KmxCond.streamRule(kmxParams.getKmxInitParams())
                .get()
                .fieldGroupId(fieldGroupId)
                .fieldId(fieldId)
                .build();

        try {
            KmxClient.sync(cond);
        } catch (Exception e) {
            log.error("", e);
            return false;
        }

        return true;
    }

    private void addIdField(List<Field> fields) {
        Field idField1 = new Field();
        idField1.setId(MyConstant.KMX_ID_FIELD_FACTORY_NAME);
        idField1.setName(MyConstant.KMX_ID_FIELD_FACTORY_NAME);
        idField1.setDescription(MyConstant.KMX_ID_FIELD_FACTORY_NAME_DES);
        idField1.setIdField(true);
        idField1.setValueType(ValueType.STRING);
        fields.add(idField1);

        Field idField2 = new Field();
        idField2.setId(MyConstant.KMX_ID_FIELD_EQUIPMENT_NAME);
        idField2.setName(MyConstant.KMX_ID_FIELD_EQUIPMENT_NAME);
        idField2.setDescription(MyConstant.KMX_ID_FIELD_EQUIPMENT_NAME_DES);
        idField2.setIdField(true);
        idField2.setValueType(ValueType.STRING);
        fields.add(idField2);

        Field idField3 = new Field();
        idField3.setId(MyConstant.KMX_ID_FIELD_MACHINE_NAME);
        idField3.setName(MyConstant.KMX_ID_FIELD_MACHINE_NAME);
        idField3.setDescription(MyConstant.KMX_ID_FIELD_MACHINE_NAME_DES);
        idField3.setIdField(true);
        idField3.setValueType(ValueType.STRING);
        fields.add(idField3);

        Field idField4 = new Field();
        idField4.setId(MyConstant.KMX_ID_FIELD_CHANNEL_TYPE);
        idField4.setName(MyConstant.KMX_ID_FIELD_CHANNEL_TYPE);
        idField4.setDescription(MyConstant.KMX_ID_FIELD_CHANNEL_TYPE_DES);
        idField4.setIdField(true);
        idField4.setValueType(ValueType.STRING);
        fields.add(idField4);

        Field idField6 = new Field();
        idField6.setId(MyConstant.KMX_ID_FIELD_CHANNEL_ID);
        idField6.setName(MyConstant.KMX_ID_FIELD_CHANNEL_ID);
        idField6.setDescription(MyConstant.KMX_ID_FIELD_CHANNEL_ID_DES);
        idField6.setIdField(true);
        idField6.setValueType(ValueType.STRING);
        fields.add(idField6);

        Field idField7 = new Field();
        idField7.setId(MyConstant.KMX_ID_FIELD_DATA_TYPE);
        idField7.setName(MyConstant.KMX_ID_FIELD_DATA_TYPE);
        idField7.setDescription(MyConstant.KMX_ID_FIELD_DATA_TYPE_DES);
        idField7.setIdField(true);
        idField7.setValueType(ValueType.STRING);
        fields.add(idField7);
    }

}
